#pragma once

#include <vector>
#include <string>
#include <sstream>
#if _MSVC_LANG >= 202002
#include <format>
#endif // __cplusplus == 202002L

namespace Utils
{
    void RemoveFloatStringTailingZeros(std::string &str);
    void RemoveFloatStringTailingZeros(std::wstring &str);

    template<typename T>
    std::wstring NumericToStringW(T val)
    {
        std::wstringstream ss;
        ss << val;
        return ss.str();
    }

    template<typename T>
    std::string NumericToString(T val)
    {
        std::stringstream ss;
        ss << val;
        return ss.str();
    }

    template<typename T, std::enable_if_t<std::is_floating_point<T>::value, bool> = 0>
    std::wstring NumericToStringW(T val, int precision, bool bRemoveTailingZeros = false)
    {
#ifdef _FORMAT_
        auto ret = std::format(L"{:.{}f}", val, precision);
        if(bRemoveTailingZeros) {
            RemoveFloatStringTailingZeros(ret);
        }
        return ret;
#else
        long double a = val;
        {
            std::wstringstream ss;
            ss.precision(precision);
            ss << std::fixed << val;
            a = std::stold(ss.str());
        }
        std::wstringstream ss;
        ss << a;
        return ss.str();
#endif // _FORMAT_
    }

    template<typename T, std::enable_if_t<std::is_floating_point<T>::value, bool> = 0>
    std::string NumericToString(T val, int precision, bool bRemoveTailingZeros = false)
    {
#ifdef _FORMAT_
        auto ret = std::format("{:.{}f}", val, precision);
        if(bRemoveTailingZeros) {
            RemoveFloatStringTailingZeros(ret);
        }
        return ret;
#else
        long double a = val;
        {
            std::stringstream ss;
            ss.precision(precision);
            ss << std::fixed << val;
            a = std::stold(ss.str());
        }
        std::stringstream ss;
        ss << a;
        return ss.str();
#endif // _FORMAT_
    }

    template<bool>
    std::wstring NumericToStringW(bool val)
    {
        return val ? L"1" : L"0";
    }

    template<bool>
    std::string NumericToString(bool val)
    {
        return val ? "1" : "0";
    }

    template<typename T>
    T StringToNumeric(const std::wstring &value)
    {
        T val;
        std::wstringstream ss;
        ss << value.c_str();
        ss >> val;
        return val;
    }

    template<typename T>
    T StringToNumeric(const std::string &value)
    {
        T val;
        std::stringstream ss;
        ss << value.c_str();
        ss >> val;
        return val;
    }

    template<bool>
    bool StringToNumeric(const std::wstring &value)
    {
        int val{};
        std::wstringstream ss;
        ss << value.c_str();
        ss >> val;
        return val != 0;
    }

    template<bool>
    bool StringToNumeric(const std::string &value)
    {
        int val{};
        std::stringstream ss;
        ss << value.c_str();
        ss >> val;
        return val != 0;
    }

    // source from https://github.com/ekg/split/blob/master/split.h
    // thanks to Evan Teran, http://stackoverflow.com/questions/236129/how-to-split-a-string/236803#236803
    // split a string on a single delimiter character (delim)
    std::vector<std::string> &SplitString(const std::string &s, std::vector<std::string> &elems, char delim);
    std::vector<std::string> SplitString(const std::string &s, char delim);

    // split a string on any character found in the string of delimiters (delims)
    std::vector<std::string> &SplitString(const std::string &s, std::vector<std::string> &elems, const std::string &delims);
    std::vector<std::string> SplitString(const std::string &s, const std::string &delims);

    // split a wstring on a single delimiter w-character (delim)
    std::vector<std::wstring> &SplitString(const std::wstring &s, std::vector<std::wstring> &elems, wchar_t delim);
    std::vector<std::wstring> SplitString(const std::wstring &s, wchar_t delim);

    // split a wstring on any character found in the wstring of delimiters (delims)
    std::vector<std::wstring> &SplitString(const std::wstring &s, std::vector<std::wstring> &elems, const std::wstring &delims);
    std::vector<std::wstring> SplitString(const std::wstring &s, const std::wstring &delims);
} //namespace Utils
